/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2021  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef GRUB_LOONGARCH64_LINUX_HEADER
#define GRUB_LOONGARCH64_LINUX_HEADER 1

#include <grub/types.h>

/* LoongArch linux kernel type */
#define GRUB_LOONGARCH_LINUX_BAD 0
#define GRUB_LOONGARCH_LINUX_ELF 1
#define GRUB_LOONGARCH_LINUX_EFI 2

#define GRUB_LOONGSON3_BOOT_MEM_MAP_MAX 128

#define GRUB_LINUX_LOONGARCH_MAGIC_SIGNATURE  0x6E6F73676E6F6F4C /* 'Loongson' */

/* From linux/Documentation/loongarch/booting.txt
 *
 * 0-1: MZ
 * 0x28: LoongArch\0
 * 0x3c: PE/COFF头偏移
 * 0x20e:内核版本号偏移-512
 * riscv的version字段在0x20偏移处，现在LoongArch没有使用，是0
 */
struct linux_loongarch64_kernel_header
{
  grub_uint32_t code0;		/* Executable code */
  grub_uint32_t code1;		/* Executable code */
  grub_uint64_t text_offset;	/* Image load offset */
  grub_uint64_t res0;		/* reserved */
  grub_uint64_t res1;		/* reserved */
  grub_uint64_t res2;		/* reserved */
  grub_uint64_t magic;		/* Magic number, little endian, "Loongson" */
  grub_uint64_t res3;		/* reserved */
  grub_uint32_t res4;		/* reserved */
  grub_uint32_t hdr_offset;	/* Offset of PE/COFF header */
};

struct linux_loongarch64_kernel_params
{
  grub_addr_t kernel_addr; 	/* kernel entry address */
  grub_size_t kernel_size;	/* kernel size */
  grub_addr_t ramdisk_addr;	/* initrd load address */
  grub_size_t ramdisk_size;	/* initrd size */
  int         ramdisk_args_len;  /* position of initrd in linux_args */
  int         linux_argc;	/* cmdline parameters number*/
  grub_addr_t linux_argv;	/* cmdline parameters address*/
  void*       linux_args;
  void*       fdt;
};

#include <grub/efi/efi.h>
#include <grub/elfload.h>

#define GRUB_EFI_MAX_PHY_ADDRESS 0xffffffffffffULL
#define ELF32_LOADMASK (0xf0000000UL)
#define ELF64_LOADMASK (0xf000000000000000ULL)
#define FLAGS_EFI_SUPPORT_BIT 0

/*initrd info*/
#define GRUB_RD_START_STRING "rd_start=0xXXXXXXXXXXXXXXXX"
#define GRUB_RD_SIZE_STRING  "rd_size=0xXXXXXXXXXXXXXXXX"
#define GRUB_INITRD_STRING   "initrd=0xXXXXXXXXXXXXXXXX,0xXXXXXXXXXXXXXXXX"

#define FDT_ADDR_CELLS_STRING "#address-cells"
#define FDT_SIZE_CELLS_STRING "#size-cells"
#define FDT_ADDR_SIZE_EXTRA ((2 * grub_fdt_prop_entry_size (sizeof(grub_uint32_t))) + \
                             sizeof (FDT_ADDR_CELLS_STRING) + \
                             sizeof (FDT_SIZE_CELLS_STRING))

struct efi_boot_memmap {
  grub_efi_uintn_t  map_size;
  grub_efi_uintn_t  desc_size;
  grub_efi_uint32_t desc_ver;
  grub_efi_uintn_t  map_key;
  grub_efi_uintn_t  buff_size;
  grub_efi_memory_descriptor_t map[];
};

struct efi_initrd {
  grub_efi_uintn_t  base;
  grub_efi_uintn_t  size;
};

/*
 * These are set up by the setup-routine at boot-time:
 */
struct screen_info {
  grub_efi_uint8_t  orig_x;       /* 0x00 */
  grub_efi_uint8_t  orig_y;       /* 0x01 */
  grub_efi_uint16_t ext_mem_k;    /* 0x02 */
  grub_efi_uint16_t orig_video_page;  /* 0x04 */
  grub_efi_uint8_t  orig_video_mode;  /* 0x06 */
  grub_efi_uint8_t  orig_video_cols;  /* 0x07 */
  grub_efi_uint8_t  flags;        /* 0x08 */
  grub_efi_uint8_t  unused2;      /* 0x09 */
  grub_efi_uint16_t orig_video_ega_bx;/* 0x0a */
  grub_efi_uint16_t unused3;      /* 0x0c */
  grub_efi_uint8_t  orig_video_lines; /* 0x0e */
  grub_efi_uint8_t  orig_video_isVGA; /* 0x0f */
  grub_efi_uint16_t orig_video_points;/* 0x10 */

  /* VESA graphic mode -- linear frame buffer */
  grub_efi_uint16_t lfb_width;    /* 0x12 */
  grub_efi_uint16_t lfb_height;   /* 0x14 */
  grub_efi_uint16_t lfb_depth;    /* 0x16 */
  grub_efi_uint32_t lfb_base;     /* 0x18 */
  grub_efi_uint32_t lfb_size;     /* 0x1c */
  grub_efi_uint16_t cl_magic, cl_offset; /* 0x20 */
  grub_efi_uint16_t lfb_linelength;   /* 0x24 */
  grub_efi_uint8_t  red_size;     /* 0x26 */
  grub_efi_uint8_t  red_pos;      /* 0x27 */
  grub_efi_uint8_t  green_size;   /* 0x28 */
  grub_efi_uint8_t  green_pos;    /* 0x29 */
  grub_efi_uint8_t  blue_size;    /* 0x2a */
  grub_efi_uint8_t  blue_pos;     /* 0x2b */
  grub_efi_uint8_t  rsvd_size;    /* 0x2c */
  grub_efi_uint8_t  rsvd_pos;     /* 0x2d */
  grub_efi_uint16_t vesapm_seg;   /* 0x2e */
  grub_efi_uint16_t vesapm_off;   /* 0x30 */
  grub_efi_uint16_t pages;        /* 0x32 */
  grub_efi_uint16_t vesa_attributes;  /* 0x34 */
  grub_efi_uint32_t capabilities;     /* 0x36 */
  grub_efi_uint32_t ext_lfb_base; /* 0x3a */
  grub_efi_uint8_t  _reserved[2]; /* 0x3e */
} __attribute__((packed));

#define GRUB_VIDEO_TYPE_EFI     0x70
#define GRUB_VIDEO_CAPABILITY_SKIP_QUIRKS    (1 << 0)
#define GRUB_VIDEO_CAPABILITY_64BIT_BASE     (1 << 1)    /* Frame buffer base is 64-bit */

/* From arch/loongarch/include/asm/mach-loongson64/boot_param.h */
struct _extention_list_hdr {
    grub_uint64_t		signature;
    grub_uint32_t  		length;
    grub_uint8_t   		revision;
    grub_uint8_t   		checksum;
	union {
      struct  _extention_list_hdr *next;
      grub_uint64_t  next_offset;
    };

} GRUB_PACKED;

struct boot_params_interface {
    grub_uint64_t		signature;  /* {"B", "P", "I", "0", "1", ... } */
    grub_efi_system_table_t *systemtable;
    union {
      struct _extention_list_hdr	*extlist;
      grub_uint64_t  extlist_offset;
    };
    grub_uint64_t flags;
}GRUB_PACKED;

struct loongsonlist_mem_map {
    struct _extention_list_hdr	header;	/* {"M", "E", "M"} */
    grub_uint8_t  map_count;
    struct memmap {
	grub_uint32_t mem_type;
	grub_uint64_t mem_start;
	grub_uint64_t mem_size;
    } GRUB_PACKED map[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
}GRUB_PACKED;

grub_err_t
finalize_efi_params_linux (struct linux_loongarch64_kernel_params *kernel_params);

grub_err_t
grub_linux_loongarch_elf_linux_boot_image (struct linux_loongarch64_kernel_params
					   *kernel_params);

void*
grub_linux_loongarch_alloc_virtual_mem_addr (grub_addr_t addr,
					     grub_size_t size,
					     grub_err_t *err);

void*
grub_linux_loongarch_alloc_virtual_mem_align (grub_size_t size,
					      grub_size_t align,
					      grub_err_t *err);

void*
grub_linux_loongarch_alloc_initrd_mem_align (grub_size_t size,
                          grub_size_t align,
                          grub_err_t *err);

void
grub_linux_loongarch_elf_relocator_unload (void);

int
grub_linux_loongarch_elf_get_boot_params (struct boot_params_interface **boot_params);

grub_err_t
grub_linux_loongarch_elf_boot_params (struct boot_params_interface *boot_params);

grub_err_t
grub_linux_loongarch_elf_load_kernel (grub_elf_t elf, const char *filename);

#endif /* ! GRUB_LOONGARCH64_LINUX_HEADER */
